/*
 * Copyright 2017 - 2025 the original author or authors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see [https://www.gnu.org/licenses/]
 */

package infra.expression.spel;

import org.jspecify.annotations.Nullable;

import java.text.MessageFormat;

/**
 * Contains all the messages that can be produced by the Spring Expression Language.
 * Each message has a kind (info, warn, error) and a code number. Tests can be written to
 * expect particular code numbers rather than particular text, enabling the message text
 * to more easily be modified and the tests to run successfully in different locales.
 *
 * <p>When a message is formatted, it will have this kind of form, capturing the prefix
 * and the error kind:
 *
 * <pre class="code">EL1005E: Type cannot be found 'String'</pre>
 *
 * @author Andy Clement
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
 * @since 4.0
 */
public enum SpelMessage {

  TYPE_CONVERSION_ERROR(Kind.ERROR, 1001,
          "Type conversion problem, cannot convert from {0} to {1}"),

  CONSTRUCTOR_NOT_FOUND(Kind.ERROR, 1002,
          "Constructor call: No suitable constructor found on type {0} for arguments {1}"),

  CONSTRUCTOR_INVOCATION_PROBLEM(Kind.ERROR, 1003,
          "A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"),

  METHOD_NOT_FOUND(Kind.ERROR, 1004,
          "Method call: Method {0} cannot be found on type {1}"),

  TYPE_NOT_FOUND(Kind.ERROR, 1005,
          "Type cannot be found ''{0}''"),

  FUNCTION_NOT_DEFINED(Kind.ERROR, 1006,
          "Function ''{0}'' could not be found"),

  PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1007,
          "Property or field ''{0}'' cannot be found on null"),

  PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1008,
          "Property or field ''{0}'' cannot be found on object of type ''{1}'' - maybe not public or not valid?"),

  PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1009,
          "Property or field ''{0}'' cannot be set on null"),

  PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010,
          "Property or field ''{0}'' cannot be set on object of type ''{1}'' - maybe not public or not writable?"),

  METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011,
          "Method call: Attempted to call method {0} on null context object"),

  CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1012,
          "Cannot index into a null value"),

  NOT_COMPARABLE(Kind.ERROR, 1013,
          "Cannot compare instances of {0} and {1}"),

  INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(Kind.ERROR, 1014,
          "Incorrect number of arguments for function ''{0}'': {1} supplied but function takes {2}"),

  INVALID_TYPE_FOR_SELECTION(Kind.ERROR, 1015,
          "Cannot perform selection on input data of type ''{0}''"),

  RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(Kind.ERROR, 1016,
          "Result of selection criteria is not boolean"),

  BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(Kind.ERROR, 1017,
          "Right operand for the 'between' operator has to be a two-element list"),

  INVALID_PATTERN(Kind.ERROR, 1018,
          "Pattern is not valid ''{0}''"),

  PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1019,
          "Projection is not supported on the type ''{0}''"),

  ARGLIST_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1020,
          "The argument list of a lambda expression should never have getValue() called upon it"),

  EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1021,
          "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"),

  FUNCTION_REFERENCE_CANNOT_BE_INVOKED(Kind.ERROR, 1022,
          "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"),

  EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR, 1023,
          "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"),

  ARRAY_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1024,
          "The array has ''{0}'' elements, index ''{1}'' is invalid"),

  COLLECTION_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1025,
          "The collection has ''{0}'' elements, index ''{1}'' is invalid"),

  STRING_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1026,
          "The string has ''{0}'' characters, index ''{1}'' is invalid"),

  INDEXING_NOT_SUPPORTED_FOR_TYPE(Kind.ERROR, 1027,
          "Indexing into type ''{0}'' is not supported"),

  INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(Kind.ERROR, 1028,
          "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"),

  EXCEPTION_DURING_METHOD_INVOCATION(Kind.ERROR, 1029,
          "A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"),

  OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(Kind.ERROR, 1030,
          "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"),

  PROBLEM_LOCATING_METHOD(Kind.ERROR, 1031,
          "Problem locating method {0} on type {1}"),

  SETVALUE_NOT_SUPPORTED(Kind.ERROR, 1032,
          "setValue(ExpressionState, Object) not supported for ''{0}''"),

  MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1033,
          "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"),

  EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR, 1034,
          "A problem occurred whilst attempting to set the property ''{0}'': {1}"),

  NOT_AN_INTEGER(Kind.ERROR, 1035,
          "The value ''{0}'' cannot be parsed as an int"),

  NOT_A_LONG(Kind.ERROR, 1036,
          "The value ''{0}'' cannot be parsed as a long"),

  INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1037,
          "First operand to matches operator must be a string. ''{0}'' is not"),

  INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1038,
          "Second operand to matches operator must be a string. ''{0}'' is not"),

  FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1039,
          "Only static methods can be called via function references. " +
                  "The method ''{0}'' referred to by name ''{1}'' is not static."),

  NOT_A_REAL(Kind.ERROR, 1040,
          "The value ''{0}'' cannot be parsed as a double"),

  MORE_INPUT(Kind.ERROR, 1041,
          "After parsing a valid expression, there is still more data in the expression: ''{0}''"),

  RIGHT_OPERAND_PROBLEM(Kind.ERROR, 1042,
          "Problem parsing right operand"),

  NOT_EXPECTED_TOKEN(Kind.ERROR, 1043,
          "Unexpected token. Expected ''{0}'' but was ''{1}''"),

  OOD(Kind.ERROR, 1044,
          "Unexpectedly ran out of input"),

  NON_TERMINATING_DOUBLE_QUOTED_STRING(Kind.ERROR, 1045,
          "Cannot find terminating \" for string"),

  NON_TERMINATING_QUOTED_STRING(Kind.ERROR, 1046,
          "Cannot find terminating '' for string"),

  MISSING_LEADING_ZERO_FOR_NUMBER(Kind.ERROR, 1047,
          "A real number must be prefixed by zero, it cannot start with just ''.''"),

  REAL_CANNOT_BE_LONG(Kind.ERROR, 1048,
          "Real number cannot be suffixed with a long (L or l) suffix"),

  UNEXPECTED_DATA_AFTER_DOT(Kind.ERROR, 1049,
          "Unexpected data after ''.'': ''{0}''"),

  MISSING_CONSTRUCTOR_ARGS(Kind.ERROR, 1050,
          "The arguments '(...)' for the constructor call are missing"),

  RUN_OUT_OF_ARGUMENTS(Kind.ERROR, 1051,
          "Unexpectedly ran out of arguments"),

  UNABLE_TO_GROW_COLLECTION(Kind.ERROR, 1052,
          "Unable to grow collection"),

  UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE(Kind.ERROR, 1053,
          "Unable to grow collection: unable to determine list element type"),

  UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR, 1054,
          "Unable to dynamically create a List to replace a null value"),

  UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR, 1055,
          "Unable to dynamically create a Map to replace a null value"),

  UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR, 1056,
          "Unable to dynamically create instance of ''{0}'' to replace a null value"),

  NO_BEAN_RESOLVER_REGISTERED(Kind.ERROR, 1057,
          "No bean resolver registered in the context to resolve access to bean ''{0}''"),

  EXCEPTION_DURING_BEAN_RESOLUTION(Kind.ERROR, 1058,
          "A problem occurred when trying to resolve bean ''{0}'': ''{1}''"),

  INVALID_BEAN_REFERENCE(Kind.ERROR, 1059,
          "@ or & can only be followed by an identifier or a quoted name"),

  TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(Kind.ERROR, 1060,
          "Expected the type of the new array to be specified as a String but found ''{0}''"),

  INCORRECT_ELEMENT_TYPE_FOR_ARRAY(Kind.ERROR, 1061,
          "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"),

  MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED(Kind.ERROR, 1062,
          "Using an initializer to build a multi-dimensional array is not currently supported"),

  MISSING_ARRAY_DIMENSION(Kind.ERROR, 1063,
          "A required array dimension has not been specified"),

  INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1064,
          "Array initializer size does not match array dimensions"),

  UNEXPECTED_ESCAPE_CHAR(Kind.ERROR, 1065,
          "Unexpected escape character"),

  OPERAND_NOT_INCREMENTABLE(Kind.ERROR, 1066,
          "The expression component ''{0}'' does not support increment"),

  OPERAND_NOT_DECREMENTABLE(Kind.ERROR, 1067,
          "The expression component ''{0}'' does not support decrement"),

  NOT_ASSIGNABLE(Kind.ERROR, 1068,
          "The expression component ''{0}'' is not assignable"),

  MISSING_CHARACTER(Kind.ERROR, 1069,
          "Missing expected character ''{0}''"),

  LEFT_OPERAND_PROBLEM(Kind.ERROR, 1070,
          "Problem parsing left operand"),

  MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071,
          "A required selection expression has not been specified"),

  EXCEPTION_RUNNING_COMPILED_EXPRESSION(Kind.ERROR, 1072,
          "An exception occurred whilst evaluating a compiled expression"),

  FLAWED_PATTERN(Kind.ERROR, 1073,
          "Failed to efficiently evaluate pattern ''{0}'': consider redesigning it"),

  EXCEPTION_COMPILING_EXPRESSION(Kind.ERROR, 1074,
          "An exception occurred while compiling an expression"),

  MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
          "Array declares too many elements, exceeding the threshold of ''{0}''"),

  MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076,
          "Repeated text is too long, exceeding the threshold of ''{0}'' characters"),

  MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077,
          "Regular expression is too long, exceeding the threshold of ''{0}'' characters"),

  MAX_CONCATENATED_STRING_LENGTH_EXCEEDED(Kind.ERROR, 1078,
          "Concatenated string is too long, exceeding the threshold of ''{0}'' characters"),

  MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079,
          "SpEL expression is too long, exceeding the threshold of ''{0}'' characters"),

  VARIABLE_ASSIGNMENT_NOT_SUPPORTED(Kind.ERROR, 1080,
          "Assignment to variable ''{0}'' is not supported"),

  NEGATIVE_REPEATED_TEXT_COUNT(Kind.ERROR, 1081,
          "Repeat count ''{0}'' must not be negative"),

  /** @since 5.0 */
  UNSUPPORTED_CHARACTER(Kind.ERROR, 1082,
          "Unsupported character ''{0}'' ({1}) encountered in expression"),

  /** @since 5.0 */
  EXCEPTION_DURING_INDEX_READ(Kind.ERROR, 1083,
          "A problem occurred while attempting to read index ''{0}'' in ''{1}''"),

  /** @since 5.0 */
  EXCEPTION_DURING_INDEX_WRITE(Kind.ERROR, 1084,
          "A problem occurred while attempting to write index ''{0}'' in ''{1}''");

  private final Kind kind;

  private final int code;

  private final String message;

  SpelMessage(Kind kind, int code, String message) {
    this.kind = kind;
    this.code = code;
    this.message = message;
  }

  /**
   * Produce a complete message including the prefix and with the inserts
   * applied to the message.
   *
   * @param inserts the inserts to put into the formatted message
   * @return a formatted message
   */
  public String formatMessage(@Nullable Object... inserts) {
    StringBuilder formattedMessage = new StringBuilder();
    formattedMessage.append("EL").append(this.code);
    if (this.kind == Kind.ERROR) {
      formattedMessage.append('E');
    }
    formattedMessage.append(": ");
    formattedMessage.append(MessageFormat.format(this.message, inserts));
    return formattedMessage.toString();
  }

  /**
   * Message kinds.
   */
  public enum Kind {
    INFO, WARNING, ERROR
  }

}
